package com.only4play.jpa.support.interceptor;

import com.only4play.auth.AuthenticatedUser;
import com.only4play.common.model.entity.BaseEntityFieldName;
import com.only4play.common.model.entity.RowStatus;
import com.only4play.jpa.support.entity.AggregateEntity;
import org.apache.commons.lang3.tuple.Pair;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;

/**
 * 自动填充拦截器
 *
 * @author liyuncong
 * @date 2023/11/17 14:18
 **/
@Component
public class AggregateAutofillInterceptor extends EmptyInterceptor {

    private AuthenticatedUser<?> currentUser() {
        return AuthenticatedUser.currentUser()
            .orElse(new AuthenticatedUser<>("anonymous", "anonymous", "anonymous"));
    }

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        return fillAuditable(entity, propertyNames, state);
    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
        return fillAuditable(entity, currentUser(), propertyNames, currentState);
    }

    private boolean fillAuditable(Object auditable, String[] parameterNames, Object[] states) {
        return fillAuditable(auditable, currentUser(), parameterNames, states);
    }

    private boolean fillAuditable(
        Object entity, AuthenticatedUser<?> user, String[] parameterNames, Object[] states
    ) {
        if (!(entity instanceof AggregateEntity aggregateEntity)) {
            return false;
        }
        List<Pair<String, Object>> values = new ArrayList<>(5);
        if (Objects.isNull(aggregateEntity.getRowStatus())) {
            values.add(Pair.of(BaseEntityFieldName.ROW_STATUS, RowStatus.OK));
        }
        LocalDateTime now = LocalDateTime.now();
        String userId = user.getUserId();
        values.add(Pair.of(BaseEntityFieldName.UPDATED_AT, now));
        values.add(Pair.of(BaseEntityFieldName.UPDATED_BY, userId));
        values.add(Pair.of(BaseEntityFieldName.CREATED_AT, now));
        values.add(Pair.of(BaseEntityFieldName.CREATED_BY, userId));
        updateSates(values, parameterNames, states);
        return true;
    }

    private void updateSates(
        List<Pair<String, Object>> values, String[] parameters, Object[] states
    ) {
        IntStream
            .range(0, parameters.length)
            .mapToObj(index ->
                Pair.of(index,
                    values
                        .stream()
                        .filter(p -> p.getLeft().equalsIgnoreCase(parameters[index]))
                        .findFirst()
                        .orElse(null)))
            .filter(item -> Objects.nonNull(item.getRight()))
            .forEach(item -> states[item.getLeft()] = item.getRight().getRight());
    }
}
